#include "WdHttpServer.h"

WdHttpServer::WdHttpServer() 
{
    
}

WdHttpServer::WdHttpServer(WdHttpServer& other)
{
    this->stop_ = other.stop_;
    this->mgr_ = other.mgr_;
}

WdHttpServer::~WdHttpServer() 
{
    mg_mgr_free(&mgr_);
}

static const char *s_web_directory = ".";

WdRouter* WdHttpServer::g_router = nullptr;

static void RequestHandlerCallbackEx(struct mg_connection *c, int ev, void *ev_data, void *fn_data) 
{
    if (ev == MG_EV_HTTP_MSG) 
    {
        struct mg_http_message *hm = (struct mg_http_message *) ev_data;
        printf("%s %s\n", std::string(hm->method.ptr, hm->method.len).c_str(),
            std::string(hm->uri.ptr, hm->uri.len).c_str());
        if (mg_http_match_uri(hm, "/api/f1")) 
        {
            mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);  // Serve REST
        } 
        else if (mg_http_match_uri(hm, "/ws")) 
        {
            // Upgrade to websocket. From now on, a connection is a full-duplex
            // Websocket connection, which will receive MG_EV_WS_MSG events.
            mg_ws_upgrade(c, hm, NULL);
        } 
        else if (mg_http_match_uri(hm, "/api/f2/*")) 
        {
            mg_http_reply(c, 200, "", "{\"result\": \"%.*s\"}\n", (int) hm->uri.len,
                hm->uri.ptr);
        }
        else 
        {
            struct mg_http_serve_opts opts = {s_web_directory, nullptr};
            mg_http_serve_dir(c, (struct mg_http_message *) ev_data, &opts);
        }
    } 
    else if (ev == MG_EV_WS_MSG) 
    {
        // Got websocket frame. Received data is wm->data. Echo it back!
        struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
        printf("ws flag: 0x%02x, data: %s\n", wm->flags,
            std::string(wm->data.ptr, wm->data.len).c_str());
        mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
        mg_iobuf_delete(&c->recv, c->recv.len);
    }
    (void) fn_data;
}

void WdHttpServer::RequestHandlerCallback(struct mg_connection *conn, int event, void *request, void *func) 
{
    if (event == MG_EV_HTTP_MSG) 
    {
        struct mg_http_message *msg = (struct mg_http_message *)request;
        //
        std::string url = std::string(msg->uri.ptr, msg->uri.len);

        RequestHandler handler = WdHttpServer::g_router->GetRequestHandlerByUrl(url);
        if (handler != nullptr)
        {
            handler(conn, request, func, "{status: 0}");
        }

    } 
    else if (event == MG_EV_WS_MSG) 
    {
        // Got websocket frame. Received data is wm->data. Echo it back!
        struct mg_ws_message *wm = (struct mg_ws_message *) request;
        printf("ws flag: 0x%02x, data: %s\n", wm->flags,
            std::string(wm->data.ptr, wm->data.len).c_str());
        mg_ws_send(conn, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
        mg_iobuf_delete(&conn->recv, conn->recv.len);
    }
    //
    (void)func;
}

WdHttpServer WdHttpServer::bind(const std::string &addr) 
{
    //std::function<void(struct mg_connection *, int, void *, void *)> callback = std::bind(&WdHttpServer::RequestEventHandler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
    stop_ = true;
    mg_log_set("3");  // Set to 3 to enable debug
    mg_mgr_init(&mgr_); 
    mg_http_listen(&mgr_, addr.c_str(), WdHttpServer::RequestHandlerCallback, NULL);
    
    return (*this);
}

WdHttpServer WdHttpServer::router(WdRouter* router)
{
    WdHttpServer::g_router = router;
    return (*this);
}

int32_t WdHttpServer::MongooseCallback() 
{
    while (!stop_) 
    {
        //printf("continue\n");
        mg_mgr_poll(&mgr_, 1000);  // Infinite event loop
    }

    return 0;
}

int32_t WdHttpServer::run() 
{
    stop_ = false;
    listen_future_ = std::async([this] { 
        return this->MongooseCallback(); 
    });
    //
    try 
    {
        listen_future_.wait();
    } 
    catch (...) 
    {
        printf("listen error\n");
    }

    return 0;
}